/**
  renderer.c - Created by Timothy Morey on 3/19/2012.

  This file provides the implementation for the functions defined in renderer.h.
*/

#ifdef __WINDOWS__
#include <Windows.h>
#endif

#include <GL/gl.h>

#include "logger.h"
#include "renderer.h"

#ifdef __WINDOWS__
#pragma warning(disable:4996)
#endif


/*****************************************************************************
 * Local definitions
 *****************************************************************************/

int GetCellValue(DataGridSchema schema, char* buf, int len, int i, Variant* value);


/*****************************************************************************
 * Implementation of renderer.h functions
 *****************************************************************************/

// TODO: this only works on float grids with byte masks
int DrawMaskedShadedGridFrame(GridMaskDef maskdef, ColorRamp ramp,
                              DataGridSchema valueSchema, int xdim, int ydim,
                              Variant minval, Variant maxval,
                              char* valueBuffer, int valuelen,
                              DataGridSchema maskSchema, 
                              char* maskBuffer, int masklen,
                              char* pixbuf, int pixbuflen)
{
  int retval = 0;

  if(valueBuffer && maskBuffer)
  {
    int i, r, c, width, height;
    Variant value, mask;
    int maskcell = 0;
    int* pixmap = (int*)pixbuf;

    width = valueSchema.Dims[xdim].Length;
    height = valueSchema.Dims[ydim].Length;

    if(xdim < ydim)
    {
      // Then we must transpose the grid
      for(r = 0; r < height; r++)
      {
        for(c = 0; c < width; c++)
        {
          i = c * height + r;
          GetCellValue(valueSchema, valueBuffer, valuelen, i, &value);
          GetCellValue(maskSchema, maskBuffer, masklen, i, &mask);
          maskcell = 0;

          if((maskdef.Negative && VariantInRange(mask, maskdef.MaskMin, maskdef.MaskMax)) ||
              ((!maskdef.Negative) && (!VariantInRange(mask, maskdef.MaskMin, maskdef.MaskMax))))
          {
            maskcell = 1;
          }

          i = r * width + c;

          if(maskcell)
            pixmap[i] = 0x00000000;
          else
            pixmap[i] = GetColor(ramp, value, minval, maxval);
        }
      }
    }
    else
    {
      for(i = 0; i < width * height; i++)
      {
        GetCellValue(valueSchema, valueBuffer, valuelen, i, &value);
        GetCellValue(maskSchema, maskBuffer, masklen, i, &mask);
        maskcell = 0;

        if((maskdef.Negative && VariantInRange(mask, maskdef.MaskMin, maskdef.MaskMax)) ||
            ((!maskdef.Negative) && (!VariantInRange(mask, maskdef.MaskMin, maskdef.MaskMax))))
        {
          maskcell = 1;
        }
          
        if(maskcell)
          pixmap[i] = 0x00000000;
        else
          pixmap[i] = GetColor(ramp, value, minval, maxval);
      }
    }

    glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixmap);
    retval = 1;
  }

  return retval;
}

int DrawPixmapFrame(char* buf, int width, int height, unsigned int format, unsigned int type)
{
  int retval = 0;

  if(buf && width > 0 && height > 0)
  {
    glDrawPixels(width, height, format, type, buf);
  }

  return retval;
}

int DrawShadedGridFrame(ColorRamp ramp, DataGridSchema valueSchema, 
                        int xdim, int ydim, Variant minval, Variant maxval,
                        char* valueBuffer, int valuelen,
                        char* pixbuf, int pixbuflen)
{
  int retval = 0;

  if(valueBuffer)
  {
    Variant value;
    int i, r, c, width, height;
    int* pixmap = (int*)pixbuf;

    width = valueSchema.Dims[xdim].Length;
    height = valueSchema.Dims[ydim].Length;

    if(xdim < ydim)
    {
      // Then we must transpose the grid
      for(r = 0; r < height; r++)
      {
        for(c = 0; c < width; c++)
        {
          i = c * height + r;
          GetCellValue(valueSchema, valueBuffer, valuelen, i, &value);

          i = r * width + c;

          if(VariantTypeNull != valueSchema.FillValue.Type &&
             VariantIsNearlyEqual(value, valueSchema.FillValue))
          {
            pixmap[i] = 0x00000000;
          }
          else
          {
            pixmap[i] = GetColor(ramp, value, minval, maxval);
          }
        }
      }
    }
    else
    {
      for(i = 0; i < width * height; i++)
      {
        GetCellValue(valueSchema, valueBuffer, valuelen, i, &value);

        if(VariantTypeNull != valueSchema.FillValue.Type &&
            VariantIsNearlyEqual(value, valueSchema.FillValue))
        {
          pixmap[i] = 0x00000000;
        }
        else
        {
          pixmap[i] = GetColor(ramp, value, minval, maxval);
        }
      }
    }

    glDrawPixels(width, height, GL_RGBA, GL_UNSIGNED_BYTE, pixmap);
    retval = 1;
  }

  return retval;
}


/*****************************************************************************
 * Implementation of local functions
 *****************************************************************************/

int GetCellValue(DataGridSchema schema, char* buf, int len, int i, Variant* value)
{
  int retval = 0;
  char logmsg[256];

  if(buf && value)
  {
    value->Type = schema.DataType;
    retval = 1;

    switch(value->Type)
    {
      case VariantTypeByte:
        value->Value.ByteVal = ((unsigned char*)buf)[i]; break;
      case VariantTypeChar:
        value->Value.CharVal = buf[i]; break;
      case VariantTypeShort:
        value->Value.ShortVal = ((short*)buf)[i]; break;
      case VariantTypeInt:
        value->Value.IntVal = ((int*)buf)[i]; break;
      case VariantTypeFloat:
        value->Value.FloatVal = ((float*)buf)[i]; break;
      case VariantTypeDouble:
        value->Value.DoubleVal = ((double*)buf)[i]; break;
      default:
        sprintf(logmsg, "GetCellValue: invalid variant type: %d", value->Type);
        WriteMessage(LogLevelError, logmsg);
        retval = 0;
        break;
    }
  }

  return retval;
}